package com.shenhaofeng.app.download;

import android.content.Context;

import com.shenhaofeng.app.exception.InitializeException;
import com.shenhaofeng.app.log.AppLog;

import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.List;
import java.util.Queue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.regex.Pattern;


/**
 * 文件下载器,管理下载任务,将Request转换为可执行的下载任务,在程序启动时读取为下载完成的临时文件
 * Created by 沈豪烽 on 2016/9/14.
 */
public final class Downloader {


    private static final String TAG = "Downloader";

    /**
     * 下载器实例
     */
    private static Downloader INSTANCE;

    /**
     * 上下文对象
     */
    private Context mContext;

    /**
     * 下载配置
     */
    private Config config;

    /**
     * 全局的回调管理
     */
    private CallbackImpl mCallback = new CallbackImpl();

    /**
     * 该队列保存了从本地读取的下载任务
     */
    private Queue<DownloadExecutor> downloadExecutors = new LinkedBlockingQueue<>();


    /**
     * 禁止用户自行实例化下载器
     *
     * @param context
     * @param config
     */
    private Downloader(Context context, Config config) {
        this.mContext = context;
        this.config = config;
        loadDownloadDisk();
    }


    /**
     * 初始化Downloader,配置基础配置，该方法必须被调用,否则后续方法都是无效的,请在Application的onCreate中初始化
     *
     * @param context
     * @param config
     */
    public static void init(Context context, Config config) {
        synchronized (Downloader.class) {
            if (INSTANCE == null) {
                INSTANCE = new Downloader(context.getApplicationContext(), config);
            } else {
                throw new UnsupportedOperationException("init()只能调用一次");
            }
        }
    }


    /**
     * 默认的初始化方法
     * @param context
     */
    public static void init(Context context) {
        init(context, new Config(context.getApplicationContext()));
    }


    /**
     * 获取下载器实例
     *
     * @return
     * 下载器实力
     */
    public static Downloader getDownloader() {
        if (INSTANCE == null) {
            throw new InitializeException("需要获取Downloader，必须要调用init()来初始化");
        }
        return INSTANCE;
    }


    /**
     * 在注册全局下载回调,组侧后必须调用{@link #unregisterCallback(Callback)}来注销该注册
     * @param callback
     * 观察回调对象
     */
    public void registerCallback(Callback callback) {
        mCallback.addCallback(callback);
    }


    /**
     * 注销下载回调
     * @param callback
     * 观察回调对象
     */
    public void unregisterCallback(Callback callback) {
        mCallback.removeCallback(callback);
    }


    /**
     * 获取初始化后在下载文件夹内以前尚未被执行完成的下载任务
     * @return
     */
    public Queue<DownloadExecutor> getLocalDownloadExecutor() {
        return downloadExecutors;
    }


    /**
     * 添加一个下载任务到任务队列,这不代表下载任务立即将会被执行，
     *
     * @param request
     * @return
     */
    public DownloadExecutor createExecutor(final DownloadRequest request) throws IOException {
        return new DownloadExecutor(this, request, mCallback);
    }


    /**
     * 获取配置对象
     * @return
     */
    protected Config getConfig() {
        return config;
    }


    /**
     * 初始化时加载硬盘上的下载任务
     */
    private void loadDownloadDisk() {
        File[] files = config.downloadPath().listFiles(new FilenameFilter() {
            @Override
            public boolean accept(File dir, String name) {
                String str = "^(\\[.*\\])\\$(\\[.*\\])\\$(\\[[0-9]*\\]).downloading.temp$";
                return name.matches(str);
            }
        });
        for (int i = 0; i < files.length; i++) {
            File downloadFile = files[i];
            String parameterString = downloadFile.getName().substring(0, downloadFile.getName().length() - ".downloading.temp".length());
            Pattern pattern = Pattern.compile("\\$");
            String[] parameters = pattern.split(parameterString);
            try {
                String url = URLDecoder.decode(parameters[0].substring(1, parameters[0].length() - 1), "utf8");
                String fileName = parameters[1].substring(1, parameters[1].length() - 1);
                DownloadRequest request = new RequestBuilder()
                        .url(url)
                        .fileName(fileName)
                        .build();
                DownloadExecutor downloadExecutor = new DownloadExecutor(this, request, mCallback);
                downloadExecutors.add(downloadExecutor);
            } catch (UnsupportedEncodingException e) {
                AppLog.e(TAG, "无法解析下载连接地址", e);
            }

        }
    }


    /**
     * 全局的下载状态监听
     */
    class CallbackImpl implements Callback {

        private List<Callback> callbacks = new ArrayList<>();


        public void addCallback(Callback callback) {
            callbacks.add(callback);
        }


        public void removeCallback(Callback callback) {
            callbacks.remove(callback);
        }


        @Override
        public void onDownloadStart(DownloadExecutor executor) {
            for (int i = 0; i < callbacks.size(); i++) {
                callbacks.get(i).onDownloadStart(executor);
            }
        }


        @Override
        public void onDownloadSuccess(DownloadResult result) {
            for (int i = 0; i < callbacks.size(); i++) {
                callbacks.get(i).onDownloadSuccess(result);
            }
        }


        @Override
        public void onDownloadStop(DownloadExecutor executor) {
            for (int i = 0; i < callbacks.size(); i++) {
                callbacks.get(i).onDownloadStop(executor);
            }
        }


        @Override
        public void onDownloadCancel(DownloadExecutor executor) {
            for (int i = 0; i < callbacks.size(); i++) {
                callbacks.get(i).onDownloadCancel(executor);
            }
        }


        @Override
        public void onDownloadFail(DownloadExecutor executor, Throwable throwable) {
            for (int i = 0; i < callbacks.size(); i++) {
                callbacks.get(i).onDownloadFail(executor, throwable);
            }
        }


        @Override
        public void onDownloadProgress(DownloadExecutor executor, long maxCount, long count) {
            for (int i = 0; i < callbacks.size(); i++) {
                callbacks.get(i).onDownloadProgress(executor, maxCount, count);
            }
        }
    }



}
